using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Data.SqlClient;
using System.Data;

namespace ErrorDumper.Writing {
    public sealed class SqlExceptionWriter : IExceptionWriter {

        public static string ConnectionString {
            get {
                var connectionString = ExceptionDataSource.Current.ConnectionString;

                if (string.IsNullOrWhiteSpace(connectionString))
                    throw new InvalidOperationException(
                        "ConnectionString property of ExceptionDataSource is not provided.");

                return connectionString;
            }
        }

        const int _propertyNameMaxSize = 446;
        const int _propertyValueMaxSize = 450;

        public string WriteException(ExceptionPropertyCollection properties, string content) {
            var sql = new StringBuilder("begin tran;\r\nset nocount on;\r\ninsert [ExceptionContent] ([WriteUtc],[Content]) values (getutcdate(),@Content);\r\nset @Handle = @@identity;\r\n");
            var _params = new List<SqlParameter>();
            const string handleParam = "@Handle";

            _params.Add(new SqlParameter(handleParam, SqlDbType.BigInt, 8, ParameterDirection.Output, true, 0, 0, null, DataRowVersion.Default, null));
            _params.Add(new SqlParameter("@Content", SqlDbType.Xml, 0, ParameterDirection.Input, true, 0, 0, null, DataRowVersion.Default, !string.IsNullOrEmpty(content) ? (object)content : DBNull.Value));

            int propertyIndex = 0;
            if (properties != null && properties.Count > 0)
                foreach (var property in properties) {
                    var nameParam = "@n" + propertyIndex.ToString(CultureInfo.InvariantCulture);
                    var valueParam = "@v" + propertyIndex.ToString(CultureInfo.InvariantCulture);

                    sql.Append("insert [ExceptionProperty] ([Handle],[Name],[Value]) values (@Handle,");
                    sql.Append(nameParam);
                    sql.Append(",");
                    sql.Append(valueParam);
                    sql.AppendLine(");");

                    var propertyName = property.Key;
                    _params.Add(new SqlParameter(nameParam, SqlDbType.VarChar, _propertyNameMaxSize, ParameterDirection.Input, true, 0, 0, null, DataRowVersion.Default, propertyName.Length <= _propertyNameMaxSize ? propertyName : propertyName.Substring(0, _propertyNameMaxSize)));

                    var propertyValue = property.Value;
                    _params.Add(new SqlParameter(valueParam, SqlDbType.VarChar, _propertyValueMaxSize, ParameterDirection.Input, true, 0, 0, null, DataRowVersion.Default, string.IsNullOrEmpty(propertyValue) ? (object)DBNull.Value : propertyValue.Length <= _propertyValueMaxSize ? propertyValue : propertyValue.Substring(0, _propertyValueMaxSize)));

                    propertyIndex++;
                }

            sql.Append("commit tran;");

            using (var conn = new SqlConnection(ConnectionString)) {
                conn.Open();
                using (var cmd = new SqlCommand(sql.ToString(), conn)) {
                    foreach (var param in _params)
                        cmd.Parameters.Add(param);

                    cmd.ExecuteNonQuery();

                    return GetHandle((long)cmd.Parameters[handleParam].Value);
                }
            }
        }

        public static readonly string HandleScheme = "sql";

        public static bool TryParseHandle(string handle, out long value) {
            var prefix = HandleScheme + ":";
            if (!string.IsNullOrEmpty(handle) && handle.Length > prefix.Length && handle.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase))
                return long.TryParse(handle.Substring(4, prefix.Length - 4), out value);
            value = 0;
            return false;
        }

        public static string GetHandle(long value) {
            return HandleScheme + ":" + value;
        }
    }
}